package com.gitee.wsl.net.server.shortener

import com.gitee.wsl.ext.base.currentTime
import com.gitee.wsl.ext.string.encryptMD5
import io.ktor.server.application.Application
import io.ktor.http.HttpStatusCode
import io.ktor.server.request.receive
import io.ktor.server.response.respond
import io.ktor.server.response.respondRedirect
import io.ktor.server.routing.get
import io.ktor.server.routing.post
import io.ktor.server.routing.routing
import kotlinx.datetime.Instant
import timber.log.Timber

// String extension
fun String.encodeToID(truncateLength: Int = 6): String = this.encryptMD5()

// Request object
data class Request(val url: String) {
    fun toResponse(): Response = Response(url, url.encodeToID())
}

data class Stat(val clicksOverTime: MutableList<Instant> = mutableListOf())

// Response object
data class Response(val originalURL: String, private val id: String, val stat: Stat = Stat()) {
    val shortURL: String = "/$id"
}

class RoleBasedAuthConfiguration{

}

fun Application.configureShortRouting(
     responseByID: MutableMap<String, Response> = mutableMapOf<String, Response>()
) {
    fun getShortURL(url: String, truncateLength: Int = 6): String {
        val id = url.encodeToID()

        val retrievedResponse = responseByID[id]
        if (retrievedResponse != null && retrievedResponse.originalURL != url) {
            // collision spotted !
            return getShortURL(url, truncateLength + 1)
        }

        return id
    }

    routing {
        get("/{id}") {
            val id = call.parameters["id"]
            val retrievedResponse = id?.let { responseByID[it] }

            if (id.isNullOrBlank() || retrievedResponse == null) {
                return@get call.respondRedirect("https://www.google.com")
            }

            // add current date to the current response stats
            retrievedResponse.stat.clicksOverTime.add(currentTime)

            Timber.d("redirect to: ${retrievedResponse.originalURL}")
            call.respondRedirect(retrievedResponse.originalURL)
        }

        get("/api/v1/url/{id}/stat") {
            val id = call.parameters["id"]
            val retrievedResponse = id?.let { responseByID[it] }

            if (id.isNullOrBlank() || retrievedResponse == null) {
                return@get call.respond(HttpStatusCode.NoContent)
            }

            call.respond(retrievedResponse.stat)
        }

        post("/api/v1/encode") {
            // Deserialize JSON body to Request object
            val request = call.receive<Request>()

            // find the Response object if it already exists
            val shortURL = getShortURL(request.url)
            val retrievedResponse = responseByID[shortURL]
            if (retrievedResponse != null) {
                // cache hit
                Timber.d("cache hit $retrievedResponse")
                return@post call.respond(retrievedResponse)
            }

            // cache miss
            val response = request.toResponse()
            responseByID[shortURL] = response
            Timber.d("cache miss $response")

            // Serialize Response object to JSON body
            call.respond(response)
        }
    }
}